/**
 @Name：布局模块 tradition-layout，简写tlt
 @Author：xiaowf
 @Date: 2019/11/21
 */
!function(){
  'use strict';

  var entry = getEntryName() || 'tlt';
  window[entry] = {
    ver: '1.0'
  };

  /**
   * [获取API入口名字]
   * 用标签<script type="text/javascript" src="uilayout.js?xwf"></script>载入本模块
   * 入口名字为xwf，即脚本url中?后面的参数
   * @return {[String]}
   */
  function getEntryName() {
    return getConfigFilePath().split('?')[1];
  }

  /**
   * [获取当前模块的url]
   * 直接取了最后一个script标签，没有遍历查找
   * @return {[type]} [description]
   */
  function getConfigFilePath() {
    var configPath = document.getElementsByTagName('script');
    return configPath[configPath.length-1].src;
  }

  /**
   * [获取节点类型ui-type]
   * @param  {[DOM]}    node [DOM节点]
   * @return {[string]}      [类型名，如h-layout]
   */
  function getNodeType(node) {
    if (isElement(node)) {
      return node.getAttribute('ui-type') || '';
    }
    return '';
  }

  /**
   * [判断节点是否是容器]
   * @param  {[DOM]}   node [DOM节点]
   * @return {Boolean}      [true/false]
   */
  function isContainer(node) {
    var type = getNodeType(node);
    return type.indexOf('-layout')+7 == type.length;
  }

  /**
   * [判断是否是可布局的元素，包括容器]
   * @param  {[DOM]}   node [DOM节点]
   * @return {Boolean}      [true/false]
   */
  function isElement(node) {
    return node && 1 == node.nodeType && 'SCRIPT' != node.nodeName;
  }

  /**
   * [解析维度定义]
   * @param  {[String]} def [定义字符串，如123,20%,fill]
   * @return {[Object]}     [维度对象]
   */
  function parseDimDefine(def) {
    var defobj = {val: 0, type: 'fixed'};
    if (def) {
      if (isDigit(def)) {
        //数字
        defobj.val = parseInt(def);
        defobj.type = 'fixed';
      } else if (def.substr(def.length-1) == '%') {
        //百分比
        defobj.val = parseFloat(def)/100.0;
        defobj.type = 'percent';
      } else if ('fill' == def) {
        //填充
        defobj.type = 'fill';
      }
    }
    return defobj;
  }

  /**
   * [将维度定义转为html属性表示，是parseDimDefine的反函数]
   * @param  {[Object]} dim [维度定义对象]
   * @return {[String]}     [属性字符串]
   */
  function dim2String(dim) {
    switch(dim.type) {
      case 'fixed':
        return ''+dim.val;
      case 'percent':
        return ''+dim.val+'%';
      case 'fill':
        return 'fill';
      default:
        return '';
    }
  }

  /**
   * [初始化布局的递归部分]
   * @param  {[Array]} layoutA    [存放布局信息的数组，输出项]
   * @param  {[DOM]}   node       [要初始化的DOM节点]
   * @param  {[Array]} rootlayout [递归第一次调用时的数组，用于判断当前是否在布局容器内]
   * @return {[void]}
   */
  function _initLayout(layoutA, node, rootlayout) {
    if (!isElement(node)) return;
    if ($(node).is(":hidden")) return;

    initNodeWidthAndHeight(node);

    var cur = {node: node, type: getNodeType(node), children:[]};
    var iamlayout = isContainer(node);
    var parentislayout = (layoutA != rootlayout);
    if (parentislayout || iamlayout) {
      //1、上级是容器，自身是不是布局都要记录
      //2、本身是容器，也要记载
      cur.align = $(node).attr('ui-align') || 'lc';
      cur.width = parseDimDefine($(node).attr('ui-width'));
      cur.height = parseDimDefine($(node).attr('ui-height'));
      cur.gap = parseDimDefine($(node).attr('ui-gap'));
      layoutA.push(cur);
    }

    //对不在容器中的子项，也要遍历其子项，以找到子项中的所有布局
    //对这些游离的布局（上级不是布局），要记录到根布局中
    if (node.childNodes) {
      for(var i=0; i<node.childNodes.length; ++i) {
        _initLayout(iamlayout ? cur.children : rootlayout, node.childNodes[i], rootlayout);
      }
    }
  }

  /**
   * [判断是否是数字]
   * @param  {[String]} val [检查的字符串]
   * @return {Boolean}      [true/false]
   */
  function isDigit(val) {
    return isNaN(val)===false;
  }

  /**
   * [初始化节点的高(ui-width)宽(ui-height)]
   * @param  {[DOM]}  node [DOM节点]
   * @return {[void]}
   */
  function initNodeWidthAndHeight(node) {
    var def = $(node).attr('ui-width');
    if (def) {
      if (isDigit(def)) {
        //数字
        if (def != $(node).width()) {
          $(node).css('width', def+'px');
        }
      }
    }

    def = $(node).attr('ui-height');
    if (def) {
      if (isDigit(def)) {
        //数字
        if (def != $(node).height()) {
          $(node).css('height', def+'px');
        }
      }
    }
  }

  /**
   * [初始化容器的百分比设置，以适应网页缩放]
   * @param  {[Object]} layout [容器布局定义]
   * @return {[void]}
   */
  function initContainerWidthAndHeight(layout) {
    var r, v, node = layout.node;
    if ('percent' == layout.width.type) {
      r = layout.width.val;
      v = Math.round(r*$(node).parent().width());
      $(node).css('width', (v - widthSpace(node)+'px'));
    }
    if ('percent' == layout.height.type) {
      r = layout.height.val;
      v = Math.round(r*$(node).parent().height());
      $(node).css('height', (v - heightSpace(node)+'px'));
    }
  }

  /**
   * [计算得到维度对应的数值(长/宽)
   * 三种情况
   * 1、数值
   * 2、百分数，根据传入的range计算
   * 3、fill，根据range和less两个值计算，less表示剩余需要分配的份数]
   * @param  {[Object]} def   [维度定义对象]
   * @param  {[Number]} range [维度取值范围，即100%]
   * @param  {[Number]} less  [可选，只在填充布局时有效，剩余待分配的次数]
   * @return {[Number]}       [维度值]
   */
  function getDimValue(def, range, less) {
    if (!def) return 0;

    switch (def.type) {
      case 'fixed':
        return def.val;
      case 'percent':
        return Math.round(def.val * range);
      case 'fill':
        if (less <= 1) {
          return range;
        } else {
          return Math.round(range / less);
        }
    }

    return 0;
  }

  /**
   * [是否是IE盒子模型]
   * @param  {[DOM]}   node [DOM节点]
   * @return {Boolean}      [true/false]
   */
  function isIEBox(node) {
    return 'border-box' == $(node).css('box-sizing');
  }

  /**
   * [计算node的水平白边(margin+border+padding)]
   * @param  {[DOM]}    node [DOM节点]
   * @param  {[Number]} w    [宽度]
   * @return {[Number]}      [水平边框margin+border+padding]
   */
  function widthSpace(node, w) {
    if (isIEBox(node)) {
      //margin
      return $(node).outerWidth(true) - $(node).outerWidth();
    } else {
      //padding+border+margin
      return $(node).outerWidth(true) - $(node).width();
    }
  }

  /**
   * [计算node的垂直白边(margin+border+padding)]
   * @param  {[DOM]}    node [DOM节点]
   * @param  {[Number]} w    [高度]
   * @return {[Number]}      [垂直边框margin+border+padding]
   */
  function heightSpace(node, w) {
    if (isIEBox(node)) {
      //margin
      return $(node).outerHeight(true) - $(node).outerHeight();
    } else {
      //padding+border+margin
      return $(node).outerHeight(true) - $(node).height();
    }
  }

  /**
   * [布局API的递归部分]
   * @param  {[Object]} layout [布局容器]
   * @param  {[Number]} w      [布局容器的布局宽度]
   * @param  {[Number]} h      [布局容器的布局高度]
   * @return {[void]}
   */
  function _doLayout(layout, w, h) {
    var oldptn = $(layout.node).css('position');
    switch (oldptn) {
      case 'static':
        $(layout.node).css({
          'position': 'relative',
          'left': '0px',
          'top': '0px'
        });
        break;
      case 'fixed':
        $(layout.node).css({
          'position': 'absolute'
        });
        break;
    }
    
    initContainerWidthAndHeight(layout);

    switch (layout.type) {
      case 'h-layout':
        _doHLayout(layout, w, h);
        break;
      case 'v-layout':
        _doVLayout(layout, w, h);
    }
  }

  /**
   * [取布局容器的水平对齐方式]
   * @param  {[String]} align [布局定义字符串]
   * @return {[String]}       [左=l 中=c 右=r]
   */
  function hAlign(align) {
    return align && align.substr(0,1) || 'l';
  }

  /**
   * [取布局容器的垂直对齐方式]
   * @param  {[String]} align [布局定义字符串]
   * @return {[String]}       [上=t 中=c 下=b]
   */
  function vAlign(align) {
    return align && align.substr(1,1) || 'c';
  }

  /**
   * [水平布局实现]
   * @param  {[Object]} container [布局容器]
   * @param  {[Number]} argw      [布局容器的宽度，可选，默认用布局容器节点的实际宽度]
   * @param  {[Number]} argh      [布局容器的高度，可选，默认用布局容器节点的实际高度]
   * @return {[void]}
   */
  function _doHLayout(container, argw, argh) {
    var node = container.node; //容器节点
    var sz = { //容器尺寸定义
      //容器大小
      w: argw || $(node).width(),
      h: argh || $(node).height(),
      //子项尺寸统计 child-total-width(height)
      ctw: 0,
      cth: 0
    };
    sz.padding = { //容器的内边
      l: parseInt($(node).css('padding-left')),
      r: parseInt($(node).css('padding-right')),
      t: parseInt($(node).css('padding-top')),
      b: parseInt($(node).css('padding-bottom'))
    };

    var gap = container.gap;
    var child, cnode, fillnum = 0;
    for (var i=0; i<container.children.length; ++i) {
      child = container.children[i];
      cnode = child.node;
      child.sz = { w: 0, h: 0 };
      
      if ('fill' == child.width.type) {
        fillnum++;
      } else if ('percent' == child.width.type) {
        child.sz.w = getDimValue(child.width, sz.w);
      } else {
        child.sz.w = $(cnode).outerWidth(true);
      }
      
      if ('fill' == child.height.type) {
        child.sz.h = sz.h;
      } else if ('percent' == child.height.type) {
        child.sz.h = getDimValue(child.height, sz.h);
      } else {
        child.sz.h = $(cnode).outerHeight(true);
      }

      sz.ctw += child.sz.w;
      if (i<container.children.length-1) {
        if ('fill' == gap.type) {
          fillnum++;
        } else {
          sz.ctw += getDimValue(gap, sz.w);
        }
      }
    }

    if (container.children.length<=0) return;

    var cur = sz.padding.l;
    var less = sz.w - sz.ctw, gapval;
    var begin, end, skip;
    switch (hAlign(container.align)) {
      case 'r': //右对齐，要反序处理子项
        cur = sz.padding.r;
        begin = container.children.length-1; end = 0; skip = -1;
        break;
      case 'c': //居中，顺序处理，但是要增加偏移量
        if (0 == fillnum) cur += Math.round(less/2.0);
      default:  //左对齐，顺序处理
        begin = 0; end = container.children.length-1; skip = 1;
    }

    for (i=begin;;i+=skip) {
      child = container.children[i];
      cnode = child.node;
      var childh = child.sz.h - heightSpace(cnode);
      var cssdef = {
        'position': 'absolute',
        'display': 'inline-block',
        'height': childh+'px',
      };

      if ('r' == hAlign(container.align)) {
        cssdef.right = cur;
        cssdef.left = '';
      } else {
        cssdef.left = cur;
        cssdef.right = '';
      }

      switch (vAlign(container.align)) {
        case 'b':
          cssdef['top'] = '';
          cssdef['bottom'] = sz.padding.b;
          break;
        case 'c':
          cssdef['bottom'] = '';
          cssdef['top'] = sz.padding.t + parseInt((sz.h - child.sz.h)/2.0);
          break;
        default:
          cssdef['bottom'] = '';
          cssdef['top'] = sz.padding.t;
      }

      var ww;
      if ('fill' == child.width.type) {
        ww = getDimValue(child.width, less, fillnum--);
        cssdef['width'] = (ww - widthSpace(cnode))+'px';
        cur += ww;
        less -= ww;
      } else if ('percent' == child.width.type) {
        cssdef['width'] = (child.sz.w - widthSpace(cnode))+'px';
        cur += child.sz.w;
      } else {
        cur += child.sz.w;
      }
      $(cnode).css(cssdef);
      if (isContainer(cnode)) {
        _doLayout(child);
      }

      if (i == end) break; //最后一个布局完成，退出循环

      //不是最后一个，要算间距
      gapval = getDimValue(gap, less, fillnum);
      cur += gapval;
      if ('fill' == gap.type) {
        fillnum--;
        less -= gapval;
      }
    }
  }

  /**
   * [垂直布局实现]
   * @param  {[Object]} container [布局容器]
   * @param  {[Number]} argw      [布局容器的宽度，可选，默认用布局容器节点的实际宽度]
   * @param  {[Number]} argh      [布局容器的高度，可选，默认用布局容器节点的实际高度]
   * @return {[void]}
   */
  function _doVLayout(container, argw, argh) {
    var node = container.node; //容器节点
    var sz = { //容器尺寸定义
      //容器大小
      w: argw || $(node).width(),
      h: argh || $(node).height(),
      //子项尺寸统计 child-total-width(height)
      ctw: 0,
      cth: 0
    };
    sz.padding = { //容器的内边
      l: parseInt($(node).css('padding-left')),
      r: parseInt($(node).css('padding-right')),
      t: parseInt($(node).css('padding-top')),
      b: parseInt($(node).css('padding-bottom'))
    };

    var gap = container.gap;
    var child, cnode, fillnum = 0;
    for (var i=0; i<container.children.length; ++i) {
      child = container.children[i];
      cnode = child.node;
      child.sz = { w: 0, h: 0 };
      
      if ('fill' == child.height.type) {
        fillnum++;
      } else if ('percent' == child.height.type) {
        child.sz.h = getDimValue(child.height, sz.h);
      } else {
        child.sz.h = $(cnode).outerHeight(true);
      }
      
      if ('fill' == child.width.type) {
        child.sz.w = sz.w;
      } else if ('percent' == child.width.type) {
        child.sz.w = getDimValue(child.width, sz.w);
      } else {
        child.sz.w = $(cnode).outerWidth(true);
      }

      sz.cth += child.sz.h;
      if (i<container.children.length-1) {
        if ('fill' == gap.type) {
          fillnum++;
        } else {
          sz.cth += getDimValue(gap, sz.h);
        }
      }
    }

    if (container.children.length<=0) return;

    var cur = sz.padding.t;
    var less = sz.h - sz.cth, gapval;
    var begin, end, skip;
    switch (vAlign(container.align)) {
      case 'b': //下对齐，要反序处理子项
        cur = sz.padding.b;
        begin = container.children.length-1; end = 0; skip = -1;
        break;
      case 'c': //居中，顺序处理，但是要增加偏移量
        if (0 == fillnum) cur += Math.round(less/2.0);
      default:  //上对齐，顺序处理
        begin = 0; end = container.children.length-1; skip = 1;
    }

    for (i=begin;;i+=skip) {
      child = container.children[i];
      cnode = child.node;
      var childw = child.sz.w - widthSpace(cnode);
      var cssdef = {
        'position': 'absolute',
        'display': 'inline-block',
        'width': childw+'px'
      };

      if ('b' == vAlign(container.align)) {
        cssdef.bottom = cur;
        cssdef.top = '';
      } else {
        cssdef.top = cur;
        cssdef.bottom = '';
      }

      switch (hAlign(container.align)) {
        case 'r':
          cssdef['left'] = '';
          cssdef['right'] = sz.padding.r;
          break;
        case 'c':
          cssdef['right'] = '';
          cssdef['left'] = sz.padding.l + Math.round((sz.w - child.sz.w)/2.0);
          break;
        default:
          cssdef['right'] = '';
          cssdef['left'] = sz.padding.l;
      }

      var ww;
      if ('fill' == child.height.type) {
        ww = getDimValue(child.height, less, fillnum--);
        cssdef['height'] = (ww - heightSpace(cnode))+'px';
        cur += ww;
        less -= ww;
      } else if ('percent' == child.height.type) {
        cssdef['height'] = (child.sz.h - heightSpace(cnode))+'px';
        cur += child.sz.h;
      } else {
        cur += child.sz.h;
      }
      $(cnode).css(cssdef);
      if (isContainer(cnode)) {
        _doLayout(child);
      }

      if (i == end) break; //最后一个布局完成，退出循环

      //不是最后一个，要算间距
      gapval = getDimValue(gap, less, fillnum);
      cur += gapval;
      if ('fill' == gap.type) {
        fillnum--;
        less -= gapval;
      }
    }
  }

  /**
   * [用json生成html]
   * @param  {[Object]} def  [json对象]
   * @param  {[Object]} func [标签处理函数对象]
   * @return {[String]} [html内容]
   */
  function json2html(def, func) {
    var props = '', tag = 'div', childs = '';
    for (var k in def) {
      if ('name' == k) {
        tag = def['name'];
      } else {
        var s;
        if (k in func) {
          s = func[k](def[k]);
        } else if (func.else) {
          s = func['else'](k, def[k]);
        } else {
          s = k+'="'+def[k]+'"';
        }
        if ('<' == s.substr(0,1)) {
          childs += s; //子节点
        } else {
          props += ' '+s; //属性
        }
      }
    }
    return '<'+tag+props+'>'+childs+'</'+tag+'>';
  }

  /*css缓存，待实现
  var _cache = {};
  function cacheCss(node, css) {
    var oldcss = _cache[node] || {};
    var newcss = $.extend({}, oldcss, css);
    _cache[node] = newcss;
  }

  function cleanCssCache() {
    _cache = {};
  }

  function applyCssCache() {
    for(var node in _cache) {
      $(node).css(_cache[node]);
    }
    _cache = {};
  }*/

  /**
   * [布局API]
   * @param  {[Array]} layoutA [布局信息]
   * @return {[void]}
   */
  window[entry].doLayout = function(layoutA) {
    //cleanCssCache();
    var bodyobj = $(document.body); 
    var oldsz = [bodyobj.width(), bodyobj.width()];
    for (var i=0; i<layoutA.length; ++i) {
      _doLayout(layoutA[i]);
    }
    var newsz = [bodyobj.width(), bodyobj.width()];
    if (oldsz[0] != newsz[0] || oldsz[1] != newsz[1]) {
      //因为第一次布局出现滚动条，影响body的尺寸改变，所以需要再次布局...
      for (var i=0; i<layoutA.length; ++i) {
        _doLayout(layoutA[i]);
      }
    }
  }

  /**
   * [初始化布局，提取布局结构，后面可用这个结构进行布局
   * 当dom发生变化，需要重新初始化
   * 返回的layout的第一层数组元素，是彼此独立的布局
   * （一个大布局中，可能会有无直接关联的子布局(彼此没有布局关联，但包含在另一个布局中))]
   * @param  {[DOM]}   node [DOM节点]
   * @return {[Array]}      [抽取出的布局数组]
   */
  window[entry].initLayout = function(node) {
    var layoutA = [];
    if (!node) node = document.body; 
    _initLayout(layoutA, node, layoutA);
    //对每个独立布局要设置尺寸改变的响应事件处理
    for (var i = layoutA.length - 1; i >= 0; i--) {
      /*$(layoutA[i].node).on('size', function() {
        ...
      });*/
    }
    return layoutA;
  }

  /**
   * [创建布局]
   * @param  {[DOM]}    node   [新建的布局添加到该节点中]
   * @param  {[Object]} layout [布局定义对象]
   * @return {[DOM]}    [新创建的节点]
   */
  window[entry].createLayout = function(node, layout) {
    var css = layout.style || '';
    layout.style = css+';position:absolute;left:0px;top:0px;right:0px;bottom:0px;';

    var funcs = {
      type: function(val) {
        return 'ui-type="'+val+'"';
      },
      gap: function(val) {
        return 'ui-gap="'+dim2String(val)+'"';
      },
      width: function(val) {
        return 'ui-width="'+dim2String(val)+'"';
      },
      height: function(val) {
        return 'ui-height="'+dim2String(val)+'"';
      },
      children: function(val) {
        var result = '';
        if ($.isArray(val)) {
          for (var i=0; i<val.length; ++i) {
            result += json2html(val[i], funcs);
          }
        }
        return result;
      }
    };
    var html = json2html(layout, funcs);
    var newnode = $(html);
    $(node).append(newnode);
    return newnode;
  }
}();
